/********************************************************************************
*                                                                               *
* kWookaExecuteProcess.cpp -- The execute process                               *
*                                                                               *
* Copyright (c) Fengren Technology(Guangzhou) Co.LTD. All rights reserved.      *
*                                                                               *
********************************************************************************/


#include "kWookaExecuteProcess.hpp"
#include "wx/wx.h"
#include "wx/process.h"
#include "wx/dir.h"
#include "kWookaProjectInfo.hpp"
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/arrimpl.cpp>
#include "netinfo.h"
#include "kWookaApplicationItemPanel.hpp"
#include "kWooka_perferences_utils.hpp"
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include "kWookaTypedResourceInfo.hpp"


WX_DEFINE_OBJARRAY(kWookaProcessExecutorArray);

static DWORD GetProcessThreadCount(DWORD th32ProcessID, LPDWORD lpdwThreadCount);

kWookaExecutorThread* kWookaProcessExecutor::m_thread = NULL;

DWORD kWookaProcessExecutor::dwProcessorCoreNum = kWookaProcessExecutor::GetProcessorNum();

wxThread::ExitCode kWookaExecutorThread::Entry() {
	m_stop = false;

	do {
		bool more = false;
		criticalSection.Enter();
		for (size_t ss = 0; ss < m_executors.Count(); ss++) {
			if (m_stop) {
				break;
			}
			kWookaProcessExecutor* executor = static_cast<kWookaProcessExecutor*> (m_executors[ss]);
			if (executor != NULL) {
				try {
					more |= executor->FetchConsoleOutput(40);
				}
				catch (...) {
				}
			}
		}
		criticalSection.Leave();
		if (!more) {
			wxThread::Sleep(20);
		}
		// wxThread::Yield();
	} while (!m_stop);
	return NULL;
}

void kWookaExecutorThread::StopThread() {
	m_stop = true;
	Wait(wxTHREAD_WAIT_YIELD);
}

void kWookaExecutorThread::AddExecutor(kWookaProcessExecutor* pExecutor) {
	if (pExecutor != NULL) {
		criticalSection.Enter();
		this->m_executors.Add(pExecutor);
		criticalSection.Leave();
	}
}

void kWookaExecutorThread::DelExecutor(kWookaProcessExecutor* pExecutor) {
	if (pExecutor != NULL) {
		size_t f = (size_t) -1;
		criticalSection.Enter();
		for (size_t ss = 0; ss < m_executors.size(); ss++) {
			kWookaProcessExecutor* pe = this->m_executors[ss];
			if (pe == pExecutor) {
				f = ss;
				break;
			}
		}
		if (f != (size_t)-1) {
			this->m_executors.RemoveAt(f, 1);
		}
		criticalSection.Leave();
	}
}

void kWookaExecutorThread::DetachAll() {
	for (size_t ss = 0; ss < m_executors.Count(); ss++) {
		m_executors.Item(ss)->Detach();
	}
}

void kWookaProcessExecutor::SendTextLog(const wxString& text) {
	if (m_attachWindow != NULL) {
		kWookaLogger* logText = new kWookaLogger();
		wxThreadEvent te(wxEVT_COMMAND_THREAD, ID_THREAD_EVENT_LOG);
		//te.SetRefData((wxObjectRefData*)logText);
		logText->logTexts.Add(text);
		te.SetPayload(logText);
		wxQueueEvent(m_attachWindow, te.Clone());
	}
}

void kWookaProcessExecutor::Create(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	m_project = proj;
	m_appitem = app;
	pid = 0;
	m_started = false;
	m_attachWindow = NULL;
	m_ins = NULL;
	m_proc = NULL;
	m_logoutput = NULL;
	m_alreadCheckedProcess = false;
	m_updatedState = kWookaEnumProcessState::Unknow;
	QueryPerformanceFrequency(&m_lgPfreq);
	m_lgOldTime.HighPart = 0L;
	m_lgOldTime.LowPart = 0;
	m_lgOldTime.QuadPart = 0L;
	m_ioOld.OtherOperationCount = 0L;
	m_ioOld.OtherTransferCount = 0L;
	m_ioOld.ReadOperationCount = 0L;
	m_ioOld.ReadTransferCount = 0L;
	m_ioOld.WriteOperationCount = 0L;
	m_ioOld.WriteTransferCount = 0L;

}

wxString kWookaProcessExecutor::GetCommandLine() {
	if (m_factory != NULL) {
		return m_factory->BuildCommandLine(this->m_project, m_appitem);
	}
	else {
		/// a default factor
		m_factory = new kWookaSpringBootExecutorFactory();
		return m_factory->BuildCommandLine(this->m_project, m_appitem);
	}
}

void kWookaProcessExecutor::Start() {
	if (m_started == false) {
		pid = 0;
		m_ProcessInitialized = false;
		wxString projDir = wxString::Format(wxT("%s\\%s\\"), m_project->workspace, m_appitem->name);
		if (!wxDir::Exists(EnsureDirectorString(projDir))) {
			wxMkdir(EnsureDirectorString(projDir));
		}
		
		wxString projTempDir = projDir + wxT("temp");
		if (!wxDir::Exists(EnsureDirectorString(projTempDir))) {
			wxMkdir(EnsureDirectorString(projTempDir));
		}
		wxProcess* proc = NULL;// wxProcess::Open(str);
		if (m_factory != NULL) {
			if (m_factory->Prepared(this, this->m_project, m_appitem)) {
				proc = m_factory->Execute(this, this->m_project, m_appitem);
			}
		}
		else {
			/// a default factor
			m_factory = new kWookaSpringBootExecutorFactory();
			if (m_factory->Prepared(this, this->m_project, m_appitem)) {
				proc = m_factory->Execute(this, this->m_project, m_appitem);
			}
		}

		m_logs.Clear(); //remove the logs buffers
		
		kWookaProcessExecutor::StartThread();
		
		if (proc != NULL) {
			m_proc = proc;
			pid = m_proc->GetPid();
			m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
			m_proc->Connect(wxEVT_END_PROCESS, wxProcessEventHandler(kWookaProcessExecutor::OnProcessEnd), NULL, this);
			m_err = m_proc->GetErrorStream();
			m_ins = m_proc->GetInputStream();
			if (m_ins != NULL) {
				wxString fileName = wxString::Format(wxT("%s\\stdout.log"), projDir);
				m_logoutput = new wxFileOutputStream(EnsureDirectorString(fileName));

				kWookaEnumProcessState pstate = GetExecuteState();
				m_thread->AddExecutor(this);
				m_started = true;
				PostExecuteStateChangedEvent(pstate);
			}
			else {
				delete m_proc;
				m_proc = NULL;
			}
		}
		else {
			if (m_factory->IsReady()) {
				UpdateState(kWookaEnumProcessState::Started);
			}
		}
	}
}

void kWookaProcessExecutor::PostExecuteStateChangedEvent(kWookaEnumProcessState oldPs) {
	if (oldPs != this->GetExecuteState()) {
		if (m_listPanel != NULL) {
			wxThreadEvent evt;
			evt.SetId(EVT_UPDATE_EXECUTE_STATE);
			wxQueueEvent(m_listPanel, evt.Clone());
		}
	}
}

void kWookaProcessExecutor::OnProcessEnd(wxProcessEvent& event) {
	m_thread->DelExecutor(this);
	if (m_proc != NULL) {
		m_proc = NULL;
	}
	if (m_ins != NULL) {
		delete m_ins;
		m_ins = NULL;
	}
	if (m_err != NULL) {
		delete m_err;
		m_err = NULL;
	}
	
	if (m_logoutput != NULL) {
		m_logoutput->Close();
		delete m_logoutput;
		m_logoutput = NULL;
	}
	m_performances.clear();
	m_logs.clear();
	m_listenerUrls.clear();
	g_slgProcessTimeOld = { 0 };
	m_ioOld = {0};
	m_netOld = NetworkPerformanceItem();

	m_started = false;
	if (m_listPanel != NULL) {
		event.SetId(ID_THREAD_EVENT_PROCESS_END);
		wxQueueEvent(m_listPanel, event.Clone());
	}
}

bool kWookaProcessExecutor::IsStarted() {
	return m_started || (m_proc == NULL && this->GetUpdateState() == kWookaEnumProcessState::Started);
}

void kWookaProcessExecutor::AddLogs(const wxString& cp) {
#ifdef USE_CACHE_LOGS
	m_logs.Add(cp);
	while (m_logs.size() > 500) {
		m_logs.RemoveAt(0);
	}
#endif
	if (m_logoutput != NULL) {
		wxMBConvUTF8 utf8;
		wxTextOutputStream tos(*m_logoutput, wxEOL_NATIVE, utf8);
		tos.WriteString(cp);
		tos.PutChar('\n');
		tos.Flush();
	}
}

/*
 * The method sequence read file and get the lines
 */
static void SequenceReadLogs(const wxString& fileName, wxArrayString& lines, int lstlines) {
	wxFileOffset offset = (wxFileOffset)lstlines * 4L * 1024L;

	if (!wxFile::Exists(EnsureDirectorString(fileName))) {
		return;
	}

	wxFileInputStream fis(EnsureDirectorString(fileName));
	if (!fis) {
		return;
	}

	wxFileOffset fileLength = fis.GetLength();
	if (fileLength > offset) {
		fis.SeekI(fileLength - offset, wxFromStart);
	}
	
	wxTextInputStream tis(fis);
	while (!fis.Eof()) {
		lines.Add(tis.ReadLine());
	}

	if (lines.Count() > lstlines) {
		lines.RemoveAt(0, lines.Count() - lstlines);
	}
}

/*
 *  Read the file from tail and find 500 first
 */
static void stdstream_read_logs(const wxString& fileName, wxArrayString& lines, int lstlines) {
	wxMBConvUTF8 utf;
	std::ifstream fis(fileName.utf8_string(), std::ios::ate);
	if (!fis) {
		return;
	}

	fis.seekg(0, fis.cur);
	long offset = 0;

	for (int i = 0; i < lstlines && fis.good(); i++) {
		std::string st;
		unsigned char ch;
		do {
			fis.seekg(-1, fis.cur);
			ch = fis.peek();
			if (ch != '\n') {
				st.insert(0, 1, ch);
			}
			offset++;
		} while (fis.good() && ch != fis.widen('\n'));
		lines.Insert(utf.cMB2WC(st.c_str()), 0);
		st.clear();
	}

	fis.clear();
	fis.close();
}

wxArrayString kWookaProcessExecutor::ReadLogs(int lstlines) {
	wxArrayString lines;
	wxString fileName = wxString::Format(wxT("%s\\%s\\stdout.log"), m_project->workspace, m_appitem->name);
	SequenceReadLogs(fileName, lines, lstlines);
	return lines;
}

bool kWookaProcessExecutor::CheckProcessReady() {
	bool ready = false;
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_proc->GetPid());
	if (hProcess != NULL) {
		ready = true;
		CloseHandle(hProcess);
	}
	return ready;
}


// use the enum method to check all process
bool kWookaProcessExecutor::EvalProcessStatus() {
	wxString cmdline = this->GetCommandLine();
	wxVector<DWORD> proc = kWookaGetProcessListByCmdline(cmdline);
	if (proc.size() > 0) {
		// So, we use the first one to attach
		pid = proc[0];
		// m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
	}
	m_alreadCheckedProcess = true;
	return false;
}

void kWookaProcessExecutor::Stop() {
	kWookaEnumProcessState pstate = GetExecuteState();
	//Execute the Factory Hook for Stop
	if (m_factory != NULL) {
		m_factory->Shutdown(this, m_project, m_appitem);
	}

	if (m_started && m_proc != NULL) {
		// just do kill for this process
		// m_proc->Detach();
		// if it's windows platform, we call openprocess and wait it to finish in 2 seconds
		// the we will send the term to process
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_proc->GetPid());

		if (hProcess != INVALID_HANDLE_VALUE) {
			wxString cmdline = GetProcessCmdLineByHandle(hProcess);
			wxLogDebug(cmdline);
		}
		this->Detach();
		wxProcess::Kill(m_proc->GetPid(), wxSIGINT, 0);
		m_thread->DelExecutor(this);
		if (hProcess != NULL) {
			WaitForSingleObject(hProcess, 2000);
			CloseHandle(hProcess);
			hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_proc->GetPid());
			// try again to kill the process
			if (hProcess != NULL) {
				TerminateProcess(hProcess, 0);
				CloseHandle(hProcess);
			}
		}
		m_started = false;

		
		// These will be delete on delete m_proc
		if (m_ins != NULL) {
			// delete m_ins;
			m_ins = NULL;
		}
		if (m_err != NULL) {
			// delete m_err;
			m_err = NULL;
		}
		

		m_performances.clear();
		m_listenerUrls.clear();
		g_slgProcessTimeOld = { 0 };
		m_ioOld = { 0 };
		m_netOld = NetworkPerformanceItem();

		if (m_hProcess != NULL) {
			CloseHandle(m_hProcess);
			m_hProcess = NULL;
		}
		if (m_proc != NULL) {
			m_proc->DeletePendingEvents();
			m_proc->Detach();
			// m_proc = NULL;
			// delete m_proc;
			m_proc = NULL;
		}
		PostExecuteStateChangedEvent(pstate);
	}
	else {
		if (m_proc == NULL && GetUpdateState() == kWookaEnumProcessState::Started) {
			UpdateState(kWookaEnumProcessState::Ended);
		}
	}
}

bool kWookaProcessExecutor::FetchConsoleOutput(int fetchesThenYied){
	if (m_proc != NULL) {
		int fs = 0;
		// wxArrayString* texts = new wxArrayString();
		kWookaLogger* logText = new kWookaLogger();
		wxCSConv gbk(wxT("GBK"));
		if (m_ins != NULL && m_ins->CanRead()) {
			wxTextInputStream tins(*m_ins, L" \t", gbk);
			while (fs < fetchesThenYied && m_ins->CanRead()) {
				wxString line = tins.ReadLine();
				AddLogs(line);
				logText->logTexts.Add(line);
				fs++;
			}
		}
		if (m_err != NULL && m_err->CanRead()) {
			wxTextInputStream teins(*m_err, L" \t", gbk);
			while (m_err->CanRead()) {
				wxString line = teins.ReadLine();
				AddLogs(line);
				logText->logTexts.Add(line);
			}
		}
		if (logText->logTexts.size() > 0) {
			if (m_attachWindow != NULL) {
				wxThreadEvent te(wxEVT_COMMAND_THREAD, ID_THREAD_EVENT_LOG);
				//te.SetRefData((wxObjectRefData*)logText);
				te.SetPayload(logText);
				wxQueueEvent(m_attachWindow, te.Clone());
			}
			else {
				delete logText;
			}
		}
		else {
			delete logText;
		}
		return fs > 0;
	}
	return false;
}

void kWookaProcessExecutor::StartThread() {
	if (m_thread == NULL) {
		m_thread = new kWookaExecutorThread();
		m_thread->Run();
	}
}

void kWookaProcessExecutor::StopThread() {
	if (m_thread != NULL) {
		m_thread->StopThread();
		delete m_thread;
		m_thread = NULL;
	}
}

void kWookaProcessExecutor::Attach(wxWindow* loggerWindow) {
	if (m_thread != NULL) {
		m_thread->DetachAll();
	}
	m_attachWindow = loggerWindow;
}

void kWookaProcessExecutor::Detach() {
	m_attachWindow = NULL;
}

kWookaProcessPerformanceInfo kWookaProcessExecutor::GetProcessPerformance() {
	kWookaProcessPerformanceInfo info;
	wxDateTime now = wxDateTime::Now();
	PROCESS_MEMORY_COUNTERS pmc;
	info.success = false;
	info.timestamp = now.FormatISOTime();
	if (m_hProcess != NULL) {
		GetProcessMemoryInfo(m_hProcess, &pmc, sizeof(pmc));
		info.memoryUsed = pmc.WorkingSetSize;
		DWORD dwHandleCount = 0;
		GetProcessHandleCount(m_hProcess, &dwHandleCount);
		info.handlers = dwHandleCount;
		info.threads  = GetProcessThreadCount(this->GetPid(), NULL);
		
		IO_COUNTERS ct = {0};
		
		LARGE_INTEGER lpc;

		QueryPerformanceCounter(&lpc);
		GetProcessIoCounters(m_hProcess, &ct);
		NetworkPerformanceItem ntperf;
		std::vector<std::wstring> ipports;
		GetProcessNetworkPerformance(ntperf, &ipports, this->GetPid());
	
		if (m_started) {
			if (ipports.size() > 0) {
				kWookaEnumProcessState pstate = GetExecuteState();
				m_ProcessInitialized = true;
				PostExecuteStateChangedEvent(pstate);
			}
		}

		if (m_ioOld.ReadTransferCount == 0 && m_ioOld.WriteTransferCount == 0) {
			info.diskReadSpeed = 0L;
			info.diskWriteSpeed = 0L;
			info.networkRecvSpeed = 0L;
			info.networkSendSpeed = 0L;
			m_lgOldTime = lpc;
			m_ioOld = ct;
			m_netOld = ntperf;
		}
		else {
			int time = (((lpc.QuadPart - m_lgOldTime.QuadPart)) / m_lgPfreq.QuadPart);
			info.diskReadSpeed = (ct.ReadTransferCount - m_ioOld.ReadTransferCount) / (double)time;
			info.diskWriteSpeed = (ct.WriteTransferCount -m_ioOld.WriteTransferCount) / (double)time;
			info.cpuUsages = this->GetProcessCpuPercent(m_hProcess, time * 1000);
			// info.networkRecvSpeed = ntperf.InboundBandwidth; // (ntperf.BytesIn - m_netOld.BytesIn) / (double)time;
			// info.networkSendSpeed = ntperf.OutboundBandwidth; // (ntperf.BytesOut - m_netOld.BytesOut) / (double)time;
			if (ntperf.BytesIn > m_netOld.BytesIn) {
				info.networkRecvSpeed = (ntperf.BytesIn - m_netOld.BytesIn) / (double)time;
			}
			else {
				info.networkRecvSpeed = 0;
			}
			if (ntperf.BytesOut > m_netOld.BytesOut) {
				info.networkSendSpeed = (ntperf.BytesOut - m_netOld.BytesOut) / (double)time;
			}
			else {
				info.networkSendSpeed = 0;
			}
			//wxLogDebug(wxT("IN: %I64d-%I64d BY %d"), ntperf.BytesIn, m_netOld.BytesIn, time);
			//wxLogDebug(wxT("OUT: %I64d-%I64d BY %d"), ntperf.BytesOut, m_netOld.BytesOut, time);
			//wxLogDebug(wxT("COUNT: %I64d-%I64d BY %d"), info.networkRecvSpeed, info.networkSendSpeed, time);
			m_lgOldTime = lpc;
			m_ioOld = ct;
			m_netOld = ntperf;
		}

		info.success = true;
	}

	if (info.success) {
		m_performances.push_back(info);
		while (m_performances.size() > 360) {
			m_performances.erase(m_performances.begin());
			wxLogDebug(wxT("Erase kWookaProcessPerformanceInfo."));
		}
	}
	return info;
}

wxVector<kWookaProcessPerformanceInfo>& kWookaProcessExecutor::GetAllPerformances() {
	return m_performances;
}

static DWORD GetProcessThreadCount(DWORD th32ProcessID, LPDWORD lpdwThreadCount) //
{
	HANDLE hThreadSnap;
	THREADENTRY32 th32;
	DWORD dwCount = 0;
	hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);
	if (hThreadSnap == INVALID_HANDLE_VALUE)
	{
		return 0;
	}

	th32.dwSize = sizeof(THREADENTRY32);
	if (!Thread32First(hThreadSnap, &th32))
	{
		CloseHandle(hThreadSnap);
		return 0;
	}
	do
	{
		if (th32.th32OwnerProcessID == th32ProcessID)
		{
			dwCount++;
		}
	} while (Thread32Next(hThreadSnap, &th32));
	CloseHandle(hThreadSnap);
	if (lpdwThreadCount != NULL) {
		*lpdwThreadCount = dwCount;
	}
	return dwCount;
}

/*@brief GET cpu rate
* @param [in]    hProcess            Process handle
* @param [in]    dwElepsedTime        sample time
* @return : cpu rate  fail : -1
*/
int kWookaProcessExecutor::GetProcessCpuPercent(const HANDLE hProcess, const DWORD dwElepsedTime)
{
	int nProcCpuPercent = 0;
	BOOL bRetCode = FALSE;
	FILETIME CreateTime, ExitTime, KernelTime, UserTime;
	LARGE_INTEGER lgKernelTime;
	LARGE_INTEGER lgUserTime;
	LARGE_INTEGER lgCurTime;

	bRetCode = GetProcessTimes(hProcess, &CreateTime, &ExitTime, &KernelTime, &UserTime);
	if (bRetCode)
	{
		lgKernelTime.HighPart = KernelTime.dwHighDateTime;
		lgKernelTime.LowPart = KernelTime.dwLowDateTime;
		lgUserTime.HighPart = UserTime.dwHighDateTime;
		lgUserTime.LowPart = UserTime.dwLowDateTime;

		lgCurTime.QuadPart = (lgKernelTime.QuadPart + lgUserTime.QuadPart) / 10000;
		nProcCpuPercent = (int)((lgCurTime.QuadPart - g_slgProcessTimeOld.QuadPart) * 100 / dwElepsedTime);
		g_slgProcessTimeOld = lgCurTime;
		nProcCpuPercent = nProcCpuPercent / dwProcessorCoreNum;
	}
	else
	{
		nProcCpuPercent = -1;
	}

	return nProcCpuPercent;
}

kWookaEnumProcessState kWookaProcessExecutor::GetExecuteState() {
	if (m_proc == NULL && m_hProcess == NULL && m_err == NULL && m_ins == NULL && m_started == false && m_ProcessInitialized == false) {
		return GetUpdateState();
	}
	if (m_proc == NULL && m_hProcess == NULL && m_started == false && m_ProcessInitialized == false) {
		return kWookaEnumProcessState::NoStarted;
	}
	if (m_proc != NULL && m_hProcess != NULL && m_started == true && m_ProcessInitialized == false) {
		return kWookaEnumProcessState::Starting;
	}
	if (m_proc != NULL && m_hProcess != NULL && m_started == true && m_ProcessInitialized == true) {
		return kWookaEnumProcessState::Started;
	}
	if (m_proc == NULL && m_hProcess == NULL && m_started == false && m_ProcessInitialized == true) {
		return kWookaEnumProcessState::Ended;
	}
	return GetUpdateState();
}

DWORD kWookaProcessExecutor::GetProcessorNum() {
	SYSTEM_INFO sysInfo = { 0 };
	GetSystemInfo(&sysInfo);
	return sysInfo.dwNumberOfProcessors;
}

wxString kWookaSpringBootExecutorFactory::BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString cmdline = proj->jvmConfig.jdkHome + "\\bin\\java.exe";

	if (app->jvmConfig.jvmDebugEnabled && !app->jvmConfig.jvmDebugAddress.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%s"), app->jvmConfig.jvmDebugAddress));
	}

	if (!proj->jvmConfig.jvmHeap.IsEmpty() || !app->jvmConfig.jvmHeap.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xms%s "), app->jvmConfig.jvmHeap.IsEmpty() ? proj->jvmConfig.jvmHeap : app->jvmConfig.jvmHeap));
	}
	if (!proj->jvmConfig.jvmMaxHeap.IsEmpty() || !app->jvmConfig.jvmMaxHeap.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xmx%s "), app->jvmConfig.jvmMaxHeap.IsEmpty() ? proj->jvmConfig.jvmMaxHeap : app->jvmConfig.jvmMaxHeap));
	}
	if (!proj->jvmConfig.jvmMaxPermSize.IsEmpty() || !app->jvmConfig.jvmMaxPermSize.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:MaxPermSize=%s "), app->jvmConfig.jvmMaxPermSize.IsEmpty() ? proj->jvmConfig.jvmMaxPermSize : app->jvmConfig.jvmMaxPermSize));
	}
	if (!proj->jvmConfig.jvmMaxDirectMemorySize.IsEmpty() || !app->jvmConfig.jvmMaxDirectMemorySize.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:MaxDirectMemorySize=%s "), app->jvmConfig.jvmMaxDirectMemorySize.IsEmpty() ? proj->jvmConfig.jvmMaxDirectMemorySize : app->jvmConfig.jvmMaxDirectMemorySize));
	}
	if (!proj->jvmConfig.jvmGCOptions.IsEmpty() || !app->jvmConfig.jvmGCOptions.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:%s "), app->jvmConfig.jvmGCOptions.IsEmpty() ? proj->jvmConfig.jvmGCOptions : app->jvmConfig.jvmGCOptions));
	}
	if (!proj->jvmConfig.jvmOtherOptions.IsEmpty() || !app->jvmConfig.jvmOtherOptions.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" %s "), app->jvmConfig.jvmOtherOptions.IsEmpty() ? proj->jvmConfig.jvmOtherOptions : app->jvmConfig.jvmOtherOptions));
	}

	if (!app->source.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -jar %s"), app->source));
	}

	wxStringToStringHashMap hm;
	for (size_t s = 0; s < proj->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = proj->appConfigs[s];
		if (ci.configType != wxT("env")) {
			hm[ci.configKey] = ci.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType != wxT("env")) {
			hm[ci.configKey] = ci.configValue;
		}
	}

	for (wxStringToStringHashMap::const_iterator it = hm.begin(); it != hm.end(); ++it) {
		cmdline.Append(wxString::Format(wxT(" --%s=%s"), it->first, it->second));
	}

	return cmdline;
}

wxProcess* kWookaSpringBootExecutorFactory::Execute(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString cmdline = BuildCommandLine(proj, app);
	wxExecuteEnv env;
	wxGetEnvMap(&env.env);

	//for (wxStringToStringHashMap::const_iterator it = env.env.begin(); it != env.env.end(); ++it) {
	//	wxLogDebug(wxString::Format(wxT("%s--%s"), it->first, it->second));
	//}
	env.env["NLS_LANG"] = "SIMPLIFIED CHINESE_CHINA.ZHS16GBK";
	env.env["JAVA_HOME"] = proj->jvmConfig.jdkHome;
	env.env["TEMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));
	env.env["TMP"] = EnsureDirectorString(proj->workspace + wxT("\\")  + app->name + wxT("\\temp"));

	for (size_t i = 0; i < proj->appConfigs.Count(); i++) {
		kWookaAppConfigItemInfo& it = proj->appConfigs[i];
		if (it.configType == wxT("env")) {
			env.env[it.configKey] = it.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType == wxT("env")) {
			env.env[ci.configKey] = ci.configValue;
		}
	}

	env.cwd = proj->workspace;
	

	wxLogDebug(cmdline);


	wxProcess* proc = new wxProcess(wxPROCESS_REDIRECT);
	long pid = wxExecute(cmdline, wxEXEC_ASYNC, proc, &env);
	if (pid == 0) {
		delete proc;
		return NULL;
	}
	proc->SetPid(pid);
	return proc;
}


wxString kWookaMavenExecutorFactory::BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString cmdline = proj->jvmConfig.jdkHome + "\\bin\\java.exe";
	if (!proj->jvmConfig.jvmHeap.IsEmpty() || !app->jvmConfig.jvmHeap.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xms%s "), app->jvmConfig.jvmHeap.IsEmpty() ? proj->jvmConfig.jvmHeap : app->jvmConfig.jvmHeap));
	}
	if (!proj->jvmConfig.jvmMaxHeap.IsEmpty() || !app->jvmConfig.jvmMaxHeap.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xmx%s "), app->jvmConfig.jvmMaxHeap.IsEmpty() ? proj->jvmConfig.jvmMaxHeap : app->jvmConfig.jvmMaxHeap));
	}
	if (!proj->jvmConfig.jvmMaxPermSize.IsEmpty() || !app->jvmConfig.jvmMaxPermSize.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:MaxPermSize=%s "), app->jvmConfig.jvmMaxPermSize.IsEmpty() ? proj->jvmConfig.jvmMaxPermSize : app->jvmConfig.jvmMaxPermSize));
	}
	if (!proj->jvmConfig.jvmMaxDirectMemorySize.IsEmpty() || !app->jvmConfig.jvmMaxDirectMemorySize.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:MaxDirectMemorySize=%s "), app->jvmConfig.jvmMaxDirectMemorySize.IsEmpty() ? proj->jvmConfig.jvmMaxDirectMemorySize : app->jvmConfig.jvmMaxDirectMemorySize));
	}
	if (!proj->jvmConfig.jvmGCOptions.IsEmpty() || !app->jvmConfig.jvmGCOptions.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:%s "), app->jvmConfig.jvmGCOptions.IsEmpty() ? proj->jvmConfig.jvmGCOptions : app->jvmConfig.jvmGCOptions));
	}
	if (!proj->jvmConfig.jvmOtherOptions.IsEmpty() || !app->jvmConfig.jvmOtherOptions.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" %s "), app->jvmConfig.jvmOtherOptions.IsEmpty() ? proj->jvmConfig.jvmOtherOptions : app->jvmConfig.jvmOtherOptions));
	}

	if (!app->name.IsEmpty() && !app->version.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -jar %s\\%s\\%s-%s.jar"), proj->workspace, app->name, app->name, app->version));
	}

	wxStringToStringHashMap hm;
	for (size_t s = 0; s < proj->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = proj->appConfigs[s];
		if (ci.configType != wxT("env")) {
			hm[ci.configKey] = ci.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType != wxT("env")) {
			hm[ci.configKey] = ci.configValue;
		}
	}

	for (wxStringToStringHashMap::const_iterator it = hm.begin(); it != hm.end(); ++it) {
		cmdline.Append(wxString::Format(wxT(" --%s=%s"), it->first, it->second));
	}
	return cmdline;
}



wxProcess* kWookaMavenExecutorFactory::Execute(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString cmdline = BuildCommandLine(proj, app);
	wxExecuteEnv env;
	wxGetEnvMap(&env.env);

	//for (wxStringToStringHashMap::const_iterator it = env.env.begin(); it != env.env.end(); ++it) {
	//	wxLogDebug(wxString::Format(wxT("%s--%s"), it->first, it->second));
	//}
	env.env["NLS_LANG"] = "SIMPLIFIED CHINESE_CHINA.ZHS16GBK";
	env.env["JAVA_HOME"] = proj->jvmConfig.jdkHome;
	env.env["TEMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));
	env.env["TMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));

	for (size_t i = 0; i < proj->appConfigs.Count(); i++) {
		kWookaAppConfigItemInfo& it = proj->appConfigs[i];
		if (it.configType == wxT("env")) {
			env.env[it.configKey] = it.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType == wxT("env")) {
			env.env[ci.configKey] = ci.configValue;
		}
	}

	env.cwd = proj->workspace;

	wxLogDebug(cmdline);

	wxProcess* proc = new wxProcess(wxPROCESS_REDIRECT);
	long pid = wxExecute(cmdline, wxEXEC_ASYNC, proc, &env);
	if (pid == 0) {
		delete proc;
		return NULL;
	}
	proc->SetPid(pid);
	return proc;
}

bool kWookaMavenExecutorFactory::Prepared(kWookaProcessOutputRender* executor, kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString mvncmd = FormatMavenCommand(proj, app);
	if (mvncmd == wxT("ERROR")) {
		return false;
	}
	wxExecuteEnv env;
	wxGetEnvMap(&env.env);

	//for (wxStringToStringHashMap::const_iterator it = env.env.begin(); it != env.env.end(); ++it) {
	//	wxLogDebug(wxString::Format(wxT("%s--%s"), it->first, it->second));
	//}
	env.env["NLS_LANG"] = "SIMPLIFIED CHINESE_CHINA.ZHS16GBK";
	env.env["JAVA_HOME"] = proj->jvmConfig.jdkHome;
	env.env["TEMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));
	env.env["TMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));
	env.cwd = proj->workspace;

	wxProcess* proc = new wxProcess(wxPROCESS_REDIRECT);
	long pid = wxExecute(mvncmd, wxEXEC_ASYNC, proc, &env);
	if (pid == 0) {
		delete proc;
		return false;
	}
	proc->SetPid(pid);
	wxInputStream* input = proc->GetInputStream();
	if (input != NULL) {
		wxCSConv gbk(wxT("GBK"));
		wxTextInputStream tins(*input, L" \t", gbk);
		do {
			while (input->CanRead()) {
				wxString line = tins.ReadLine();
				executor->SendTextLog(line);
			}
			wxThread::Sleep(200);
		} while (wxProcess::Exists(pid));
		// delete input;
	}

	return true;
}

void BuildExecuteEnv(wxExecuteEnv& env, kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxGetEnvMap(&env.env);

	//for (wxStringToStringHashMap::const_iterator it = env.env.begin(); it != env.env.end(); ++it) {
	//	wxLogDebug(wxString::Format(wxT("%s--%s"), it->first, it->second));
	//}
	env.env["NLS_LANG"] = "SIMPLIFIED CHINESE_CHINA.ZHS16GBK";
	env.env["JAVA_HOME"] = proj->jvmConfig.jdkHome;
	env.env["TEMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));
	env.env["TMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));

	for (size_t i = 0; i < proj->appConfigs.Count(); i++) {
		kWookaAppConfigItemInfo& it = proj->appConfigs[i];
		if (it.configType == wxT("env")) {
			env.env[it.configKey] = it.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType == wxT("env")) {
			env.env[ci.configKey] = ci.configValue;
		}
	}

	env.cwd = EnsureDirectorString(proj->workspace + wxT("\\") + app->name);
}

wxString EnsureDirectorString(const wxString& dirstr) {
	if (dirstr.Index(wxT(" ")) != wxNOT_FOUND) {
		return wxT("\"") + dirstr + wxT("\"");
	}
	return dirstr;
}

wxString kWookaGenericJavaExecutorFactory::BuildCommandLine(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString cmdline = wxT("\"") + proj->jvmConfig.jdkHome + "\\bin\\java.exe\"";

	if (app->jvmConfig.jvmDebugEnabled && !app->jvmConfig.jvmDebugAddress.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%s"), proj->jvmConfig.jvmDebugAddress));
	}

	// add library to cp
	cmdline.Append(wxT(" -cp ."));
	for (size_t s = 0; s < app->libraries.Count(); s++) {
		wxString str = app->libraries[s];
		cmdline.Append(wxT(";")).Append(str);
	}

	if (!proj->jvmConfig.jvmHeap.IsEmpty() || !app->jvmConfig.jvmHeap.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xms%s "), app->jvmConfig.jvmHeap.IsEmpty() ? proj->jvmConfig.jvmHeap : app->jvmConfig.jvmHeap));
	}
	if (!proj->jvmConfig.jvmMaxHeap.IsEmpty() || !app->jvmConfig.jvmMaxHeap.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -Xmx%s "), app->jvmConfig.jvmMaxHeap.IsEmpty() ? proj->jvmConfig.jvmMaxHeap : app->jvmConfig.jvmMaxHeap));
	}
	if (!proj->jvmConfig.jvmMaxPermSize.IsEmpty() || !app->jvmConfig.jvmMaxPermSize.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:MaxPermSize=%s "), app->jvmConfig.jvmMaxPermSize.IsEmpty() ? proj->jvmConfig.jvmMaxPermSize : app->jvmConfig.jvmMaxPermSize));
	}
	if (!proj->jvmConfig.jvmMaxDirectMemorySize.IsEmpty() || !app->jvmConfig.jvmMaxDirectMemorySize.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:MaxDirectMemorySize=%s "), app->jvmConfig.jvmMaxDirectMemorySize.IsEmpty() ? proj->jvmConfig.jvmMaxDirectMemorySize : app->jvmConfig.jvmMaxDirectMemorySize));
	}
	if (!proj->jvmConfig.jvmGCOptions.IsEmpty() || !app->jvmConfig.jvmGCOptions.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" -XX:%s "), app->jvmConfig.jvmGCOptions.IsEmpty() ? proj->jvmConfig.jvmGCOptions : app->jvmConfig.jvmGCOptions));
	}
	if (!proj->jvmConfig.jvmOtherOptions.IsEmpty() || !app->jvmConfig.jvmOtherOptions.IsEmpty()) {
		cmdline.Append(wxString::Format(wxT(" %s "), app->jvmConfig.jvmOtherOptions.IsEmpty() ? proj->jvmConfig.jvmOtherOptions : app->jvmConfig.jvmOtherOptions));
	}

	wxStringToStringHashMap hm;
	for (size_t s = 0; s < proj->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = proj->appConfigs[s];
		if (ci.configType != wxT("env")) {
			hm[ci.configKey] = ci.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType != wxT("env")) {
			hm[ci.configKey] = ci.configValue;
		}
	}

	for (wxStringToStringHashMap::const_iterator it = hm.begin(); it != hm.end(); ++it) {
		cmdline.Append(wxString::Format(wxT(" -D%s=%s"), it->first, it->second));
	}

	// add the start class
	cmdline.Append(wxT("  ")).Append(app->startClass);

	return cmdline;
}

wxProcess* kWookaGenericJavaExecutorFactory::Execute(kWookaProjectInfo* proj, kWookaAppItemInfo* app) {
	wxString cmdline = BuildCommandLine(proj, app);
	wxExecuteEnv env;
	wxGetEnvMap(&env.env);

	//for (wxStringToStringHashMap::const_iterator it = env.env.begin(); it != env.env.end(); ++it) {
	//	wxLogDebug(wxString::Format(wxT("%s--%s"), it->first, it->second));
	//}
	env.env["NLS_LANG"] = "SIMPLIFIED CHINESE_CHINA.ZHS16GBK";
	env.env["JAVA_HOME"] = proj->jvmConfig.jdkHome;
	env.env["TEMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));
	env.env["TMP"] = EnsureDirectorString(proj->workspace + wxT("\\") + app->name + wxT("\\temp"));

	for (size_t i = 0; i < proj->appConfigs.Count(); i++) {
		kWookaAppConfigItemInfo& it = proj->appConfigs[i];
		if (it.configType == wxT("env")) {
			env.env[it.configKey] = it.configValue;
		}
	}
	for (size_t s = 0; s < app->appConfigs.Count(); s++) {
		kWookaAppConfigItemInfo& ci = app->appConfigs[s];
		if (ci.configType == wxT("env")) {
			env.env[ci.configKey] = ci.configValue;
		}
	}

	if (!app->groupId.IsEmpty() && !app->source.IsEmpty()) {
		env.env[app->groupId] = EnsureDirectorString(app->source);
	}

	env.cwd = EnsureDirectorString(proj->workspace + wxT("\\") + app->name);

	wxLogDebug(cmdline);


	wxProcess* proc = new wxProcess(wxPROCESS_REDIRECT);
	long pid = wxExecute(cmdline, wxEXEC_ASYNC, proc, &env);
	if (pid == 0) {
		delete proc;
		return NULL;
	}
	proc->SetPid(pid);
	return proc;
}

